package com.gamerole.commom.adapter.slimadapter

import android.annotation.SuppressLint
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import com.shizhefei.mvc.IDataAdapter
import com.tencent.bugly.proguard.v
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.*

@SuppressLint("NotifyDataSetChanged")
open class SlimAdapter<D> : RecyclerView.Adapter<SlimViewHolder<D>>(), IDataAdapter<List<D>> {
    private lateinit var viewTypeDelegate: (D) -> Int
    private lateinit var data: MutableList<D>
    private val uiHandler: Handler = object : Handler(Looper.getMainLooper()) {
        override fun dispatchMessage(msg: Message) {
            when (msg.what) {
                WHAT_NOTIFY_DATA_SET_CHANGED -> notifyDataSetChanged()
            }
            super.dispatchMessage(msg)
        }
    }

    fun updateData(data: MutableList<D>): SlimAdapter<D> {
        if (diffCallback == null || itemCount == 0 || data.size == 0) {
            this.data = data
            if (Looper.myLooper() == Looper.getMainLooper()) {
                notifyDataSetChanged()
            } else {
                uiHandler.removeMessages(WHAT_NOTIFY_DATA_SET_CHANGED)
                uiHandler.sendEmptyMessage(WHAT_NOTIFY_DATA_SET_CHANGED)
            }
        } else {
            val diffResult = DiffUtil.calculateDiff(SlimDiffUtil(this.data, data, diffCallback))
            this.data = data
            if (Looper.myLooper() == Looper.getMainLooper()) {
                diffResult.dispatchUpdatesTo(this)
            } else {
                uiHandler.removeMessages(WHAT_NOTIFY_DATA_SET_CHANGED)
                uiHandler.sendEmptyMessage(WHAT_NOTIFY_DATA_SET_CHANGED)
            }
        }
        return this
    }

    override fun notifyDataChanged(t: List<D>, isRefresh: Boolean) {
        if (!isRefresh) {
            if (t.isNotEmpty()) {
                data.addAll(t)
                updateData(data)
            }
        } else {
            data.clear()
            data.addAll(t)
            updateData(data)
        }
    }

    override fun getData(): List<D> {
        return data
    }

    override fun isEmpty(): Boolean {
        return itemCount <= 0
    }

    private fun getItem(position: Int): D {
        return data[position]
    }

    override fun getItemCount(): Int {
        return data.size
    }

    private val creators: MutableMap<Int, (ViewGroup)->SlimTypeViewHolder<D>> = HashMap()
    private var diffCallback: SlimDiffUtil.Callback? = null

    @JvmOverloads
    fun enableDiff(diffCallback: SlimDiffUtil.Callback? = DefaultDiffCallback()): SlimAdapter<D> {
        this.diffCallback = diffCallback
        return this
    }

    override fun onBindViewHolder(holder: SlimViewHolder<D>, position: Int) {
        holder.bind(getItem(position), null)
    }

    override fun onBindViewHolder(holder: SlimViewHolder<D>, position: Int, payloads: List<Any>) {
        holder.bind(getItem(position), payloads)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SlimViewHolder<D> {
        val creator = creators[viewType]
            ?: throw IllegalArgumentException("Neither the TYPE not The DEFAULT injector found...")
        return creator.invoke(parent)
    }

    fun <V> register(
        viewType: Int,
        layoutRes: Int,
        block: (D, V, List<Any>?) -> Unit
    ): SlimAdapter<D> {
        creators[viewType]=object :(ViewGroup)->SlimTypeViewHolder<D>{
            override fun invoke(parent: ViewGroup): SlimTypeViewHolder<D> {
                return object : SlimTypeViewHolder<D>(parent, layoutRes) {
                    override fun onBind(data: D, payloads: List<Any>?) {
                        val type = getSlimInjectorActualTypeArgumentsV(block)
                        val clazz = type as Class<*>?
                        var method: Method? = null
                        try {
                            method = clazz!!.getMethod("bind", View::class.java)
                        } catch (e: NoSuchMethodException) {
                            e.printStackTrace()
                        }
                        val invoke: V
                        try {
                            invoke = method!!.invoke(null, itemView) as V
                            block.invoke(data, invoke, payloads)
                        } catch (e: IllegalAccessException) {
                            e.printStackTrace()
                        } catch (e: InvocationTargetException) {
                            e.printStackTrace()
                        }
                    }
                }
            }
        }
        return this
    }
    private fun <V> getSlimInjectorActualTypeArgumentsV(slimInjector: (D, V, List<Any>?) -> Unit): Type? {
        val interfaces = slimInjector.javaClass.genericInterfaces
        for (type in interfaces) {
            if (type is ParameterizedType) {
                val actualType = type.actualTypeArguments[1]
                return actualType as? Class<*>
                    ?: throw IllegalArgumentException("The generic type argument of SlimInjector is NOT support Generic Parameterized Type now, Please using a WRAPPER class install of it directly.")
            }
        }
        return null
    }

    fun attachTo(vararg recyclerViews: RecyclerView): SlimAdapter<D> {
        for (recyclerView in recyclerViews) {
            recyclerView.adapter = this
        }
        return this
    }

    fun viewType(block: (D) -> Int): SlimAdapter<D> {
        this.viewTypeDelegate = block
        return this
    }

    override fun getItemViewType(position: Int): Int {
        return viewTypeDelegate.invoke(data[position])
    }
    private abstract class SlimTypeViewHolder<D>(
        parent: ViewGroup,
        itemLayoutRes: Int
    ) : SlimViewHolder<D>(parent, itemLayoutRes)

    companion object {
        private const val WHAT_NOTIFY_DATA_SET_CHANGED = 1
    }
}